package top.csy8888.placeorder.service;

import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Correlation;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.InitBinder;
import top.csy8888.placeorder.common.config.RabbitConfig;
import top.csy8888.placeorder.common.exception.OrderException;
import top.csy8888.placeorder.entity.Goods;
import top.csy8888.placeorder.entity.Order;
import top.csy8888.placeorder.mapper.GoodsMapper;
import top.csy8888.placeorder.mapper.OrderMapper;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author CSY
 * @date 2025 - 09 - 28
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
    private final GoodsMapper goodsMapper;

    private final OrderMapper orderMapper;

    private final RedisTemplate<String,Object> redisTemplate;

    private final RabbitTemplate rabbitTemplate;

    /**
     * 注入缓存管理器 ，用于获取相应的缓存对象
     */
    private final CacheManager cacheManager;

    /**
     * @PostConstruct注解标注的方法将在该对象实例化之后并且依赖注入之后立即执行
     * 缓存预热
     */
    @PostConstruct
    public void initBinder() {
        List<Goods> goodsAll = goodsMapper.listGoods();
        goodsAll.forEach((goods) -> {
            // 将所有商品的库存存入Redis
            redisTemplate.opsForValue().set(GoodsEnum.STOCK_PREFIX.value() + goods.getGid(),goods.getStock());
            //过期时间为：活动时间多长过期时间就多长
            redisTemplate.expire(GoodsEnum.STOCK_PREFIX.value() + goods.getGid(),30, TimeUnit.MINUTES);
        });
    }

    /**
     * 预扣库存
     * @param gid
     */
    /*private void decrStock(int gid) {
        String strGid = GoodsEnum.KEY_PREFIX.value() + gid;
        if (redisTemplate.opsForValue().decrement(strGid) < 0) {
            redisTemplate.opsForValue().increment(strGid);
            throw new OrderException(1001,"库存不足！");
        }
    }*/

    /**
     * 使用lua脚本实现库存的扣减
     * @param gid
     */
    private void decrStock(int gid){
        //先判断本地缓存中是否标记过此售罄商品，如果标记了则表示已经卖完了
        //获取本地缓存
        Cache localCache = cacheManager.getCache("localCache");
        if (localCache.get(GoodsEnum.STOCK_PREFIX.value() + gid) != null) {
            throw new OrderException(1001,"商品已售罄！");
        }
        //创建lua脚本需要的keys集合
        //由于Redis引擎接收key是一个集合，因此只有一个key也要封装为集合对象
        List<String> keys = Collections.singletonList(GoodsEnum.STOCK_PREFIX.value() + gid);
        //创建RedisScript（Redis脚本执行器）并设置lua脚本的文件位置
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        // 指定脚本的位置
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/stock.lua")));
        //设置lua执行后返回的数据类型
        redisScript.setResultType(Long.class);
        //执行lua脚本并传入keys集合，执行后返回库存量
        Long stock = redisTemplate.execute(redisScript, keys);
        if (stock < 0) {
            //标记本地缓存
            localCache.put(GoodsEnum.STOCK_PREFIX.value() + gid, true);
            throw new OrderException(1001,"商品已售罄！");
        }
    }

    public void placeOrder(int gid){
//        if (goodsMapper.decrStock(gid) < 0){
//            throw new OrderException(1001,"库存不足！");
//        }
        //预扣库存
        decrStock(gid);
        //生成订单
        int orderId = createOrder(gid);
        //发送延迟消息
        sendDelayMessage(orderId);
    }

    private int createOrder(int gid){
        try {
            Order order = new Order();
            order.setOrderStatus(0);
            order.setUserId(1);
            order.setGid(gid);
            orderMapper.addOrder(order);
            return order.getOrderId();
        }catch (Exception e){
            redisTemplate.opsForValue().increment(GoodsEnum.STOCK_PREFIX.value() + gid);
            throw new OrderException(1002,"下单失败！");
        }
        //延迟双删
        //分布式存储——CAP
    }

    /**
     * 延迟投递消息
     * @param orderId
     */
    private void sendDelayMessage(int orderId){
        CorrelationData cdata = new CorrelationData(UUID.randomUUID().toString());
        //延迟发送
        rabbitTemplate.convertAndSend(RabbitConfig.DELAY_EXCHANGE,
                RabbitConfig.DELAY_KEY,orderId,
                //消息源处理器
                messageSourceProcessor -> {
                    //设置延迟时间
                    messageSourceProcessor.getMessageProperties().setDelayLong(RabbitConfig.DELAY_TIME);
                    //返回这个消息处理器
                    return messageSourceProcessor;
                },cdata);
    }
}
